package com.kinyx.framework.db;

import static com.kinyx.framework.db.Statics.Transient;

import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import com.kinyx.framework.processor.Context;
import com.kinyx.framework.processor.FileCreater;
import com.kinyx.framework.processor.Utils;

public final class DBResourceGenerator {

	private static final String RESOURCE_SUFFIX = "R";

	public static void generate(final Context context, final EntityTypeElement entity) throws IOException {
		final StringBuilder sb = new StringBuilder();

		// package
		sb.append(String.format("package %s;", entity.getPackageName())).append("\n");
		sb.append("\n");

		// import
		sb.append("import javax.annotation.Generated;").append("\n");
		sb.append("import javax.persistence.metamodel.StaticMetamodel;").append("\n");
		sb.append("\n");
		sb.append("import com.kinyx.framework.db.sentence.res.Column;").append("\n");
		sb.append("\n");

		// annotation
		sb.append(String.format("@Generated(\"%s\")", EntityProcessor.class.getName())).append("\n");
		sb.append(String.format("@StaticMetamodel(%s.class)", entity.getSimpleName())).append("\n");

		// class start
		sb.append(String.format("public abstract class %s {", entity.getSimpleName() + RESOURCE_SUFFIX)).append("\n");
		sb.append("\n");

		// body
		sb.append(body(context, entity));

		// class end
		sb.append("\n");
		sb.append("}");

		// create
		FileCreater.createSourceFile(context, sb.toString(), entity.getFullName() + RESOURCE_SUFFIX);
	}

	private static String body(final Context context, final EntityTypeElement entity) {
		final StringBuilder sb = new StringBuilder();

		final TypeElement entityClass = TypeElement.class.cast(entity.getElement());
		final Consumer<VariableElement> consumer = (variableElement) -> {
			sb.append("\t").append(String.format("public static final Column %s = new Column(%s.class, \"%s\");", variableElement.getSimpleName(), entity.getSimpleName(), variableElement.getSimpleName())).append("\n");
		};

		// Superclass Column
		TypeElement superclass = Utils.getSuperclass(context, entityClass);
		while ((superclass != null) && !superclass.getQualifiedName().toString().equals(Object.class.getName())) {
			sb.append("\t").append("// from ").append(superclass).append("\n");
			iterateEnclosedElements(context, superclass, consumer);
			superclass = Utils.getSuperclass(context, superclass);
			sb.append("\n");
		}
		// Column
		iterateEnclosedElements(context, entityClass, consumer);

		return sb.toString();
	}

	private static void iterateEnclosedElements(final Context context, final TypeElement entityClass, final Consumer<VariableElement> consumer) {
		final List<? extends Element> enclosedElements = entityClass.getEnclosedElements();
		context.getLogger().NOTE("iterate class[" + entityClass.getQualifiedName() + "] enclosedElements start");
		for (final Element enclosedElement : enclosedElements) {
			context.getLogger().NOTE("enclosedElement[" + enclosedElement + "]");
			if (!Utils.isField(context, enclosedElement)) { continue; }
			final VariableElement variableElement = VariableElement.class.cast(enclosedElement);
			if (Utils.containsAnnotation(context, variableElement, Transient)) { continue; }
			final Set<Modifier> modifiers = variableElement.getModifiers();
			if (modifiers.contains(Modifier.TRANSIENT) || modifiers.contains(Modifier.STATIC)) { continue; }
			consumer.accept(variableElement);
		}
		context.getLogger().NOTE("iterate class[" + entityClass.getQualifiedName() + "] enclosedElements end");
	}

}
